Skip to content

fix(dispatcher): fail-closed quando secret ausente — SEC-003#63

Merged
adm01-debug merged 1 commit into
mainfrom
fix/sec-003-fail-closed-dispatcher-cron
May 22, 2026
Merged

fix(dispatcher): fail-closed quando secret ausente — SEC-003#63
adm01-debug merged 1 commit into
mainfrom
fix/sec-003-fail-closed-dispatcher-cron

Conversation

@adm01-debug

@adm01-debug adm01-debug commented May 22, 2026

Copy link
Copy Markdown
Owner

Summary

Fix do achado SEC-003 (🟠 ALTO) da auditoria back-end sênior 2026-05-22.

_shared/dispatcher-auth.ts:240-253 (webhook-dispatcher) e :286-294 (crons) aceitavam chamadas anônimas como "legacy_no_auth" quando o secret esperado não estava em vault nem em env. Apenas console.warn era emitido. PROD está OK (os 3 secrets — WEBHOOK_DISPATCHER_SECRET, CRON_SECRET, CONNECTIONS_AUTO_TEST_SECRET — estão no vault, validado via SELECT name FROM vault.decrypted_secrets), mas qualquer ambiente clonado herdava o comportamento fail-open.

Changes

Arquivo Mudança
supabase/functions/_shared/dispatcher-auth.ts authorizeDispatcher e authorizeCron agora devolvem 503 service_misconfigured quando o secret está ausente, em vez de aceitar legacy_no_auth. Mode "legacy_no_auth" removido dos type unions DispatcherAuthMode e CronAuthMode (sem callers externos verificados via grep).
supabase/functions/_shared/dispatcher-auth.test.ts Corrige todos os testes para usar await (a função é async desde a integração do vault — testes anteriores tinham bug silencioso). Troca o teste "env não setada => legacy_no_auth" pelo equivalente fail-closed que valida status 503 e body {error:"service_misconfigured"}.
supabase/functions/webhook-dispatcher/index.ts Atualiza header comment de "Retrocompat" para "SEC-003 fail-closed" — apenas documentação.

Operação pós-merge

Obrigatório:

  • Confirmar que os 3 secrets de cron/dispatcher estão no vault de PROD (validado em 2026-05-22 via SELECT name FROM vault.decrypted_secrets — todos presentes: CONNECTIONS_AUTO_TEST_SECRET, CRON_SECRET, WEBHOOK_DISPATCHER_SECRET).

Para clones (staging, dev, preview Lovable, fork):

  • Antes do primeiro deploy de cron/dispatcher, provisionar os mesmos secrets:
    # Vault (preferido)
    SELECT vault.create_secret('valor-aleatorio', 'WEBHOOK_DISPATCHER_SECRET');
    SELECT vault.create_secret('valor-aleatorio', 'CRON_SECRET');
    SELECT vault.create_secret('valor-aleatorio', 'CONNECTIONS_AUTO_TEST_SECRET');
    
    # OU env (fallback)
    npx supabase secrets set WEBHOOK_DISPATCHER_SECRET=... --project-ref <ref>
  • Atualizar comandos das cron jobs em cron.job para usar public.get_edge_function_secret('<NOME>') no header x-cron-secret (PROD já está assim — verificar em clones).

Impacto comportamental

Cenário Antes Depois
PROD com 3 secrets no vault mode: "secret_vault" mode: "secret_vault" (sem mudança)
Cron com header correto mode: "secret" mode: "secret" (sem mudança)
Cron com header errado ❌ 401 ❌ 401 (sem mudança)
Cron sem header ❌ 401 ❌ 401 (sem mudança)
Ambiente sem secret configurado ⚠️ Aceito anonimamente 503 service_misconfigured

Test plan

  • Lint do diff manual (sem typos, imports OK)
  • grep -rn "legacy_no_auth" supabase/ src/ retorna apenas o comentário explicativo no próprio arquivo
  • Sem callers externos do mode "legacy_no_auth" (grep em auth.mode, result.mode, DispatcherAuthMode, CronAuthMode fora do _shared)
  • CI greens: Edge Functions — Deno typecheck
  • Após merge, exercitar manualmente um cron job em PROD para confirmar que mode: "secret_vault" continua funcionando (não regressão)

Notas sobre CI

3 checks do CI estão falhando em main por motivos não relacionados (issues #58, #59, #61 — typecheck baseline regression, mocks de teste quebrados, smoke E2E timeout). Este PR não introduz nada que conserte ou agrave esses problemas.

Severidade & prioridade

Atributo Valor
Severidade 🟠 Alta
Impacto 🟠 Segurança (auth bypass em ambientes não-prod)
Prioridade P0
Esforço S (≤1h, confirmado)

https://claude.ai/code/session_011Lgxm1NZGmAztRSvZHX9U3


Generated by Claude Code


Summary by cubic

Enforce fail-closed auth for webhook dispatcher and crons: when required secrets are missing, return 503 service_misconfigured instead of accepting anonymous calls (SEC-003). Prod is unchanged; clones without secrets will now get 503 until configured.

  • Bug Fixes

    • In supabase/functions/_shared/dispatcher-auth.ts, authorizeDispatcher and authorizeCron return 503 with { error: "service_misconfigured" } when the expected secret is missing; removed "legacy_no_auth" from DispatcherAuthMode and CronAuthMode.
    • Tests now await async calls and validate the 503 path; updated the header comment in webhook-dispatcher/index.ts.
  • Migration

    • Set WEBHOOK_DISPATCHER_SECRET, CRON_SECRET, and CONNECTIONS_AUTO_TEST_SECRET in vault (preferred) or env before deploying non-prod.
    • Ensure cron jobs send x-cron-secret using public.get_edge_function_secret('<NAME>').

Written for commit 1b50279. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Melhorada segurança de autorização: requisições sem segredo configurado agora são rejeitadas com erro 503.
    • Removido suporte ao modo legado de autorização sem autenticação.
  • Documentation

    • Atualizada descrição da política fail-closed (SEC-003) para webhooks e jobs cron.

Review Change Stack

Achado SEC-003 da auditoria back-end sênior 2026-05-22 (PR #55):
quando WEBHOOK_DISPATCHER_SECRET ou o secret de cron não estavam
configurados em vault nem em env, authorizeDispatcher/authorizeCron
aceitavam a chamada como "legacy_no_auth" com apenas console.warn.
Em PROD os 3 secrets já estão no vault, mas qualquer clone
(staging, dev, preview Lovable, fork) herdava acesso anônimo.

Mudanças:
- _shared/dispatcher-auth.ts: ambos authorizeDispatcher e authorizeCron
  agora devolvem 503 service_misconfigured (fail-closed) quando o secret
  não está disponível. Remove o ramo "legacy_no_auth" dos type unions
  DispatcherAuthMode e CronAuthMode.
- _shared/dispatcher-auth.test.ts: corrige tests para usar `await`
  (authorizeCron é async desde a integração do vault), substitui o teste
  "legacy_no_auth (retrocompat)" pelo teste "fail-closed 503 (SEC-003)"
  que valida status code e body.
- webhook-dispatcher/index.ts: atualiza comentário de header para refletir
  o novo comportamento fail-closed.

Operação pós-merge necessária:
- Confirmar SIM que os 3 secrets continuam no vault de PROD:
  WEBHOOK_DISPATCHER_SECRET, CRON_SECRET, CONNECTIONS_AUTO_TEST_SECRET.
  Validado em 2026-05-22 via vault.decrypted_secrets — os 3 estão lá.
- Para staging/dev clones: provisionar os mesmos secrets ANTES de
  invocar webhook-dispatcher ou qualquer cron, senão 503.

https://claude.ai/code/session_011Lgxm1NZGmAztRSvZHX9U3
@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 90711cc8-747d-40ac-866f-733c17fa50d8

📥 Commits

Reviewing files that changed from the base of the PR and between 5f4a9ec and 1b50279.

📒 Files selected for processing (3)
  • supabase/functions/_shared/dispatcher-auth.test.ts
  • supabase/functions/_shared/dispatcher-auth.ts
  • supabase/functions/webhook-dispatcher/index.ts

Walkthrough

PR implementa mudança crítica de segurança: remoção de modo legacy_no_auth (autorização anônima retrocompatível) e adoção de fail-closed para dispatcher e cron. Quando secrets não estão configurados, ambas funções retornam 503 service_misconfigured em vez de permitir acesso. Tipos, implementação, testes e documentação foram atualizados coerentemente.

Changes

Autorização Fail-Closed para Webhook Dispatcher e Jobs Cron

Layer / File(s) Summary
Atualização de tipos de auth mode
supabase/functions/_shared/dispatcher-auth.ts
DispatcherAuthMode e CronAuthMode removem legacy_no_auth, ficando apenas com modos explícitos: secret/user_jwt para dispatcher, secret/secret_vault para cron.
Implementação fail-closed em authorizeDispatcher e authorizeCron
supabase/functions/_shared/dispatcher-auth.ts
authorizeDispatcher nega com 503 quando WEBHOOK_DISPATCHER_SECRET não está definido (antes aceitava legacy_no_auth). authorizeCron nega com 503 quando secret não existe no vault ou env (antes aceitava anonymous com legacy_no_auth), incluindo log secret_not_configured indicando qual secretEnvName configurar.
Testes de authorizeCron com async/await e validação fail-closed
supabase/functions/_shared/dispatcher-auth.test.ts
Todos os testes convertidos para async/await. Teste de env não setada agora valida falha fechada (503 + error: "service_misconfigured"). Testes com header correto/incorreto/ausente/tamanho-diferente mantêm validações de status esperado (200/401). Teste de secret com base64 mantém sucesso.
Documentação de política fail-closed em webhook-dispatcher
supabase/functions/webhook-dispatcher/index.ts
Cabeçalho de autorização atualizado para SEC-003 fail-closed (2026-05-22): explicita 503 service_misconfigured quando WEBHOOK_DISPATCHER_SECRET não está configurado; remove menção de retrocompat anônimo.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sec-003-fail-closed-dispatcher-cron

Comment @coderabbitai help to get the list of available commands and usage tips.

@supabase

supabase Bot commented May 22, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project doufsxqlfjyuvxuezpln due to reaching the limit of concurrent preview branches.
Go to Project Integrations Settings ↗︎ if you wish to update this limit.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@vercel

vercel Bot commented May 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
we-dream-big Ready Ready Preview, Comment May 22, 2026 1:00am

@adm01-debug adm01-debug marked this pull request as ready for review May 22, 2026 01:24
Copilot AI review requested due to automatic review settings May 22, 2026 01:24
@adm01-debug adm01-debug merged commit b6389b5 into main May 22, 2026
24 of 30 checks passed

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

Re-trigger cubic

@adm01-debug adm01-debug review requested due to automatic review settings May 22, 2026 01:45
@adm01-debug adm01-debug deleted the fix/sec-003-fail-closed-dispatcher-cron branch May 24, 2026 18:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants